package fr.laas.knxinterface.communication;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import fr.laas.knxinterface.commands.Commands;

/**
 * Allows to transmit data from the KNX/EIB bus to OM2M
 * 
 * @author Guillaume Garzone
 */
public class TxSocket extends Thread {

	// host:port waiting for the data from the bus
	private String host;
	private int port;

	// allows to listen to the bus (through EIBD)
	private Commands commands;
	// socket used to transmit data
	private Socket socket;
	// inputStream incoming from the bus listener
	private InputStream is;
	// allows to stop the thread
	private boolean running;
	private long delayReconnect = 1000;
	private int maxAttempt = 100;

	/**
	 * Constructor WARNING: eibd must have been launched by the program before
	 * cf. StartService
	 * 
	 * @param host
	 * @param port
	 */
	public TxSocket(String host, int port) {
		this.commands = new Commands();
		this.socket = null;
		this.running = false;
		this.host = host;
		this.port = port;
	}

	/**
	 * Stops the thread from running
	 */
	public void stopTransmission() {
		this.running = false;
	}

	/**
	 * Initialization of the connection
	 */
	public boolean initConnection() {

		boolean result = false;

		// opening the socket
		try {
			socket = new Socket(this.host, this.port);
			result = true;

		} catch (UnknownHostException e) {
			System.err.println("ERROR: host unreachable");
			// e.printStackTrace() ;
			this.closeConnection();

		} catch (IOException e) {
			// in case of initialization failure, the socket is closed
			System.err.println("ERROR while opening socket");
			// e.printStackTrace() ;
			this.closeConnection();

		} catch (IllegalArgumentException i) {
			System.err
					.println("ERROR: the sending port configured is out of range.");
			i.printStackTrace();
		}
		return result;

	}

	/**
	 * Closes the connection, in case of error or when the thread stops
	 */
	public void closeConnection() {

		try {

			// closes the input stream
			if (this.is != null) {
				this.is.close();
			}
			// closes the socket
			if (this.socket != null) {
				this.socket.close();
			}

		} catch (IOException e) {
			System.err.println("ERROR while closing connection");
			e.printStackTrace();
		}

		// kills the process
		if (this.commands != null) {
			if (this.commands.getProcess() != null) {
				this.commands.getProcess().destroy();
			}
		}

	}

	/**
	 * Inits the connection and launches the service
	 */
	public void launchConnection() {

		boolean connectionEstablished = false;
		int count = 0;

		while (!connectionEstablished && this.running) {
			System.out
					.println("Establishing connection for transmission, atempt: "
							+ count);

			// System.out.println("Initialization of the connection...") ;

			// initialization of the connection
			if (!this.initConnection()) {
				System.err.println("Error while opening connection");
			} else {

				// System.out.println("Starting listening to the bus...") ;

				// listening to the bus
				this.commands.listen();
				// getting the output stream from the bus in input stream of the
				// process
				this.is = commands.getProcess().getInputStream();
				if (this.is == null) {
					System.err.println("Error while getting input stream");
				}
				connectionEstablished = true;
			}
			if (!connectionEstablished) {
				try {
					// System.out.println("DEBUG dodo") ;
					Thread.sleep(this.delayReconnect + 10 * count);
				} catch (InterruptedException e) {
					// interrupted, continue
				}

				count++;
				if (count > this.maxAttempt) {
					System.err
							.println("TX: Connection timeout, stopping transmission");
					this.stopTransmission();
				}
				this.closeConnection();
			}
		}
	}

	/**
	 * Allows to establish the connection and then send the informations from
	 * the bus
	 */
	@Override
	public void run() {

		this.running = true;

		// allows to receive the bytes
		int c = -1;

		this.launchConnection();

		while (this.running) {
			try {

				c = this.is.read();
				if (c == -1) {

					System.err
							.println("TX: ERROR while reading result from command");
					break;

				} else {

					try {
						// sending data to the gateway
						this.socket.getOutputStream().write(c);

					} catch (SocketException s) {
						System.err.println("TX: connection lost");
						// s.printStackTrace() ;

						System.out.println("Connect retry");
						this.launchConnection();
						// break ;
					}
				}

			} catch (IOException e) {

				System.err.println("TX: ERROR while reading/sending data");
				e.printStackTrace();
				break;

			}
		}
		// closing connection
		this.closeConnection();

	}
}
